#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include "src/GLRenderer.h"
#include "src/Math/Helpers.h"

typedef struct{
	float Verts[15];
	float Normal[3];
	int Index;
	int End;
	int Width;
	int Height;
}RootQuad;

char* sceneFile;

GLuint scene;
GLuint scene_Shader;

int isPoints = 0;

int indices, quadCount;
RootQuad* Quads;

/*---------------------------------------------------------------------------*/
void Draw(){
	// make sure we are using the right buffer
	glBindBuffer(GL_ARRAY_BUFFER, scene); 

	// Initialize the vertex position attribute from the vertex shader
	GLuint aPosition = glGetAttribLocation( scene_Shader, "aPosition" );
	GLuint aColor = glGetAttribLocation( scene_Shader, "aColor" );

	GLuint p = 0, c = 3;

	glVertexAttribPointer(aPosition, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), ((char*)NULL + 0));
	glVertexAttribPointer(aColor, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), ((char*)NULL + 12));

	glEnableVertexAttribArray(aPosition);
	glEnableVertexAttribArray(aColor);

	GLuint uView = glGetUniformLocation(scene_Shader, "uView");
	GLuint uProj = glGetUniformLocation(scene_Shader, "uProj");

	glUniformMatrix4fv(uView, 1, GL_TRUE, (GLfloat*)GLCam_View.ptr());
	glUniformMatrix4fv(uProj, 1, GL_TRUE, (GLfloat*)GLCam_Proj.ptr());

	if(isPoints)
		glDrawArrays(GL_POINTS, 0, indices);
	else
		glDrawArrays(GL_TRIANGLES, 0, indices);
}
/*---------------------------------------------------------------------------*/
void Keyboard(unsigned char key, int x, int y){
    switch ( key ) {
		  //case 033:
			case 'q':
				exit(1);
		        break;
		    case 'w':
		    	//VecScl(CameraDir.ptr(), 0.1f);
		    	CameraPosition = Vector4::add(CameraPosition, CameraDir);
		    	CameraPosition[3] = 1.0f;

				CameraDir = GLCam_Orientation.Multiply(Vector4(0, 0, 1, 1));
				CameraLook = Vector4::add(CameraDir, CameraPosition);
				//Running = 0;
		        break;
		    case 's':
		    	//VecScl(CameraDir.ptr(), 0.1f);
		    	CameraPosition = Vector4::sub(CameraPosition, CameraDir);
		    	CameraPosition[3] = 1.0f;

				CameraDir = GLCam_Orientation.Multiply(Vector4(0, 0, 1, 1));
				CameraLook = Vector4::add(CameraDir, CameraPosition);
				//Running = 0;
		        break;
		    case 'p':
		    	isPoints = 1;
		    	break;
		    case 't':
		    	isPoints = 0;
		    	break;
    }
}
/*---------------------------------------------------------------------------*/
void push(std::vector<float>* data, float* position, float mod[3], float* color, int pi, int ci){
	float* p = position + pi * 3;
	float* c = color + ci * 3;
	data->push_back(p[0] + mod[0]);data->push_back(p[1] + mod[1]);data->push_back(p[2] + mod[2]);
	data->push_back(c[0]);data->push_back(c[1]);data->push_back(c[2]);
}
/*---------------------------------------------------------------------------*/
void Cleanup(){}
/*---------------------------------------------------------------------------*/
void Init(){
	FILE* fp = fopen(sceneFile, "rb");
	int patchCount = 0;

	if(fp){
		fread(&patchCount, sizeof(int), 1, fp);

		indices = patchCount;

		float* positions = new float[patchCount * 3];
		float* colors = new float[patchCount * 3];

		fread(positions, sizeof(float), patchCount * 3, fp);
		fread(colors, sizeof(float), patchCount * 3, fp);

		fread(&quadCount, sizeof(int), 1, fp);
		printf("Quads: %d\n", quadCount);
		Quads = new RootQuad[quadCount];
		fread(Quads, sizeof(RootQuad), quadCount, fp);

		std::vector<float> data;

		for(int qi = 0; qi < quadCount; qi++){
			//int qi = 0;
			int off = Quads[qi].Index;
			int w = Quads[qi].Width;
			int h = Quads[qi].Height;
			printf("%d: %d X %d\n", off, w, h);

			float halfX[3], halfY[3];
			VecSub(halfX, (positions + ((off + 1) * 3)), (positions + off * 3));
			VecScl(halfX, 0.5f);
			VecPrint((positions + ((off + 1) * 3))); printf(" - "); VecPrint((positions + off * 3));
			printf(" = "); VecPrint(halfX); printf("\n");

			VecSub(halfY, (positions + ((off + w) * 3)), (positions + off * 3));
			VecScl(halfY, 0.5f);
			VecPrint((positions + ((off + w) * 3))); printf(" - "); VecPrint((positions + off * 3));
			printf(" = "); VecPrint(halfY); printf("\n");

			float LL[3] = {0}, LR[3] = {0}, UL[3] = {0}, UR[3] = {0}, Z[3] = {0};

			VecSub(LL, LL, halfX);
			VecAdd(LL, LL, halfY);

			VecAdd(LR, LR, halfX);
			VecAdd(LR, LR, halfY);

			VecSub(UL, UL, halfX);
			VecSub(UL, UL, halfY);

			VecAdd(UR, UR, halfX);
			VecSub(UR, UR, halfY);

			VecSub(halfX, (positions + ((off + 1) * 3)), (positions + off * 3));

			for(int i = Quads[qi].Index; i < Quads[qi].End; i++){
			//for(int y = 0; y < h; y++){
			//	for(int x = 0; x < w; x++){
					int ul = i;
					int ur = ul + 1;
					int ll = ul + w;
					int lr = ll + 1;

					/*if(y == h - 1){ // right
						push(&data, positions, UL, colors, i + ul, i + ul);
						push(&data, positions, UR, colors, i + ul, i + ul);
						push(&data, positions, LL, colors, i + ul, i + ll);

						push(&data, positions, LL, colors, i + ul, i + ll);
						push(&data, positions, UR, colors, i + ul, i + ul);
						push(&data, positions, LR, colors, i + ul, i + ll);
					}
					else if(x == w - 1){ // bottom
						push(&data, positions, UL, colors, i + ul, i + ul);
						push(&data, positions, UR, colors, i + ul, i + ul);
						push(&data, positions, LL, colors, i + ul, i + ll);

						push(&data, positions, LL, colors, i + ul, i + ll);
						push(&data, positions, UR, colors, i + ul, i + ul);
						push(&data, positions, LR, colors, i + ul, i + ll);
					}
					else*/{
						push(&data, positions, UL, colors, i + ul, i + ul);
						push(&data, positions, UR, colors, i + ul, i + ur);
						push(&data, positions, LL, colors, i + ul, i + ll);

						push(&data, positions, LL, colors, i + ul, i + ll);
						push(&data, positions, UR, colors, i + ul, i + ur);
						push(&data, positions, LR, colors, i + ul, i + lr);
					//}
				}
			}
			//break;
		}

		float* allData = new float[data.size()];//patchCount * 6];
		indices = data.size() / 6;
		printf("LEN %d\n", data.size());

		for(int i = 0; i < data.size(); i++){
			allData[i] = data[i];
		}

/*		for(int i = 0; i < patchCount; i++){
			memcpy(allData + i * 6, positions + i * 3, sizeof(float) * 3);
			memcpy(allData + (i * 6 + 3), colors + i * 3, sizeof(float) * 3);
			//allData[i] = data[i];
		}*/

		for(int i = 0; i < quadCount; i++){
			printf("[%d, %d)\n", Quads[i].Index, Quads[i].End);
		}

		GLuint va;
		glGenVertexArrays(1, &va);
		glBindVertexArray(va);

		glGenBuffers(1, &scene);
		glBindBuffer(GL_ARRAY_BUFFER, scene);

		glBufferData(
			GL_ARRAY_BUFFER,
			indices * 6 * sizeof(float),//data.size() * sizeof(float),
			allData,
			GL_STATIC_DRAW
		);
	}

	printf("Patches: %d\n", indices);
	scene_Shader = GLRenderInitShader("./Shaders/final.vert", "./Shaders/final.frag");
}
/*---------------------------------------------------------------------------*/
int main(int argv, char* argc[]){

	if(argv == 2){
		sceneFile = argc[1];

		printf("Creating render context...");

		GLRenderContext creation = {
			800,                // window width
			600,                // window height
			"Simple Radiosity Viewer", // window title
			argv,               // cmd line arg count
			argc,               // cmd line args
			// callback functions
			Init,
			Draw,
			Keyboard,
			Cleanup
		};

		printf("created!\n");



		GLRender_Init(creation);

		return 0;

	}
	
	printf("Incorrect number of args\n");
	return 1;
}
